package cn.blu10ph.git;

import cn.blu10ph.git.index.*;
import cn.blu10ph.tools.*;

import java.io.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

public class GitUtils {
	File rootFile;
	public GitUtils(String path){
		this.rootFile = new File(path+"/.git");
	}
	public LinkedList<String> getObjectHashs() {
		LinkedList<String> list = new LinkedList<>();
		/*if  (!rootFile .exists()  && !rootFile .isDirectory()) {					//如果Git文件夹不存在
			return list;
		}*/
		File file =new File(rootFile.getPath()+"/objects");
		if  (!file .exists()  && !file .isDirectory()) {							//如果Git对象文件夹不存在
			return list;
		}
		File[] files = file.listFiles();											//获取objects下文件列表
		for (File idxFile : files) {												//遍历文件
			if (idxFile.isDirectory()) {											//如果是文件夹
				String dirName = idxFile.getName();									//获取文件名
				if(!"info".equals(dirName) && !"pack".equals(dirName)){				//如果不是打包文件的info和pack文件夹(小米助手备份未使用Git的GC,全部文件都是object)
					File objectFileDir =new File(idxFile.getPath());
					File[] objectFiles = objectFileDir.listFiles();					//获取该文件夹下的文件列表
					for (File objectFile : objectFiles) {
						if (!objectFile.isDirectory()) {
							list.add(idxFile.getName() + objectFile.getName());		//添加文件的hash到列表
						}
					}
				}
			}
		}
		return list;
	}
	public GitObject catFile(String objectHash) {
		String dirName = objectHash.substring(0,2);									//获取文件夹名
		String fileName = objectHash.substring(2);									//获取文件名
		String filePath = rootFile.getPath()+"/objects/"+dirName+"/"+fileName;		//文件路径
		GitObject gitObject = getGitObject(filePath);
		return gitObject;
	}
	public Boolean catFile(String objectHash,String path) {
		String dirName = objectHash.substring(0,2);									//获取文件夹名
		String fileName = objectHash.substring(2);									//获取文件名
		String filePath = rootFile.getPath()+"/objects/"+dirName+"/"+fileName;		//文件路径
		return catGitObjectToPath(filePath, path+"/"+objectHash);
	}
	public GitIndex getGitIndex() {
		String filePath = rootFile.getPath()+"/index";		//文件路径

		int version = 0;
		int lenght = 0;
		Map<String , GitIndexItemMini> indexItemList = new HashMap<>();
		try{
			RandomAccessFile raf = new RandomAccessFile(filePath, "r");

			byte[] magicByte = new byte[4];
			int hasRead = 0;
			hasRead += raf.read(magicByte);
			StringBuffer objectType = new StringBuffer();
			for(byte word : magicByte){
				objectType.append((char)word);
			}
			if(!"DIRC".equals(objectType.toString())){
				System.out.println("Is Not Index File!");
				return null;
			}

			version = raf.readInt();
			hasRead+=4;
			if(version!=2){
				System.out.println("Not Support!");
				return null;
			}

			lenght = raf.readInt();
			hasRead+=4;
			//System.out.println("lenght:"+lenght);

			Boolean goon = true;
			int readItem = 0;
			do{
				hasRead+=24;
				raf.seek(hasRead);
				byte[] allMode = new byte[4];
				hasRead += raf.read(allMode);
				byte mode = 0;
				for(int i = 3; i >= 0; i--){

				}

				hasRead+=8;
				raf.seek(hasRead);
				int itemFileLenght = raf.readInt();
				hasRead+=4;
				byte[] hash = new byte[20];
				hasRead += raf.read(hash);
				String hashStr = bytes2hex(hash);
				//System.out.println("SHA-1:"+hashStr);
				short itemNameLenght = raf.readShort();
				hasRead+=2;
				//System.out.println("itemNameLenght:"+itemNameLenght);
				int byteLenght = ((itemNameLenght-2)/8+1)*8+2;
				//System.out.println("byteLenght:"+byteLenght);
				byte[] pathByte = new byte[itemNameLenght];
				hasRead += raf.read(pathByte);
				StringBuffer pathString = new StringBuffer();
				for(byte word : pathByte){
					pathString.append((char)word);
				}
				//System.out.println("ITEM" + readItem + ":" + pathString.toString());
				hasRead += byteLenght - itemNameLenght;
				raf.seek(hasRead);

				indexItemList.put(hashStr,new GitIndexItemMini(itemFileLenght,hashStr,itemNameLenght,pathString.toString()));

				readItem++;
				if(readItem==lenght){
					goon = false;
				}
			}while(goon);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		GitIndex gitIndex = new GitIndex(version,lenght,indexItemList);
		return gitIndex;
	}
	public Boolean catGitObjectToPath(String filePath,String toFilePath) {
		return getUnZLibFileToFile(filePath,toFilePath);
	}
	public GitObject getGitObject(String filePath) {
		byte[] ObjectByte = UnZLibFile(filePath);
		GitObject gitObject = getGitObject(ObjectByte);
		return gitObject;
	}
	public GitObject getGitObject(byte[] ObjectByte) {
		GitObject gitObject = null;
		int spaceNum = 0;
		StringBuffer objectType = new StringBuffer();								//GitObject类型(commit/tree/blob)
		StringBuffer objectLenght = new StringBuffer();								//内容长度
		for(int i=0;i< ObjectByte.length;i++){
			if(spaceNum<2){															//遇到分隔符的次数小于2
				if(0==ObjectByte[i] || 32==ObjectByte[i]){								//格式为"{type} {lenght}\0{content}"按空格和\0分割
					spaceNum++;
					continue;
				}
				char tempChar = (char)ObjectByte[i];									//获取字符
				if(spaceNum==0){													//如果还没遇到分割符
					objectType.append(tempChar);									//字符加入GitObject类型
				}else if(spaceNum==1){												//第一次遇到分隔符后
					objectLenght.append(tempChar);									//字符加入GitObject长度
				}
			}else if(spaceNum>=2){													//第二次遇到分割符
				try{
					//if ("blob".equals(objectType.toString())) {
					gitObject = new GitObject(									//生成GitObject,写入数据
							objectType.toString(),
							objectLenght.toString(),
							Arrays.copyOfRange(ObjectByte, i, ObjectByte.length - i)
					);
					/*}else{
						gitObject = new GitObject(
								objectType.toString(),
								objectLenght.toString(), null);
					}*/
				}catch (Exception ex){
					System.out.println("Exception:"+ex.getStackTrace());
				}
				break;
			}
		}
		return gitObject;
	}
	public byte[] UnZLibFile(String filePath) {
		byte[] ObjectByte = null;
		File file = new File(filePath);	//打开文件
		try {
			FileInputStream fis = new FileInputStream(file);						//打开文件输入流
			ObjectByte = ZLibUtils.decompress(fis);									//使用zlib解压缩
			fis.close();
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return ObjectByte;
	}
	public Boolean getUnZLibFileToFile(String filePath,String toFilePath) {
		File file = new File(filePath);	//打开文件
		FileInputStream fis;
		FileOutputStream fos;
		Boolean flg = false;
		try {
			fis = new FileInputStream(file);						//打开文件输入流
			fos = new FileOutputStream(toFilePath);						//打开文件输入流
			flg = ZLibUtils.decompress(fis,fos);									//使用zlib解压缩
			fis.close();
			fos.close();
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return flg;
	}
	/** *//**文件重命名
	 * @param path 文件目录
	 * @param oldname  原来的文件名
	 * @param newname 新文件名
	 */
	public static Boolean renameFile(String path,String oldname,String newname){
		boolean rename_flg = false;
		if(!oldname.equals(newname)){//新的文件名和以前文件名不同时,才有必要进行重命名
			File oldfile=new File(path+"\\"+oldname);
//			System.out.println("oldname:"+path+"\\"+oldname);
			File newfile=new File(path+"\\"+newname);
//			System.out.println("newname:"+path+"\\"+newname);
			if(!oldfile.exists()){
				System.out.println(newname+"文件不存在！");
				return rename_flg;//重命名文件不存在
			}
			if(newfile.exists())//若在该目录下已经有一个文件和新文件名相同，则不允许重命名
				System.out.println(newname+"已经存在！");
			else{
				rename_flg = oldfile.renameTo(newfile);
//				System.out.println("rename_flg:"+(rename_flg?"Y":"N"));
			}
		}else{
			System.out.println("新文件名和旧文件名相同...");
		}
		return rename_flg;
	}
	//http://blog.csdn.net/lmj623565791/article/details/23609929 方法3
	public static String bytes2hex(byte[] bytes)
	{
		final String HEX = "0123456789abcdef";
		StringBuilder sb = new StringBuilder(bytes.length * 2);
		for (byte b : bytes)
		{
			// 取出这个字节的高4位，然后与0x0f与运算，得到一个0-15之间的数据，通过HEX.charAt(0-15)即为16进制数
			sb.append(HEX.charAt((b >> 4) & 0x0f));
			// 取出这个字节的低位，与0x0f与运算，得到一个0-15之间的数据，通过HEX.charAt(0-15)即为16进制数
			sb.append(HEX.charAt(b & 0x0f));
		}

		return sb.toString();
	}
}
